// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::PathBuf;
use eyre::{eyre, ContextCompat};
use rerun::external::{clap::{self, Parser}, log::{error, info}, re_log::{self}};
use rerun_tools::dora_data::DataKind;
use rerun_tools_cpp::pcl_utils::{DataFormat, PointCloudPtr, PointXYZ, PointXYZI};
use dora_node_api::{self, DoraNode, Event, EventStream};

#[derive(Debug, Parser)]
#[clap(author, version, about)]
struct Args {
    /// Points data format
    #[arg(short, long, value_enum)]
    format: DataFormat,
    /// The path to dora file
    path: PathBuf,
}

fn run(mut node: DoraNode, mut events: EventStream, points: Vec<u8>, output_id: &str) -> eyre::Result<()> {
    let mut has_sent_points = true;
    loop {
        let event = match events.recv() {
            Some(input) => input,
            None => break,
        };
        match event {
            Event::Stop(_) => {
                info!("dora stop");
                return Ok(());
            },
            Event::Reload { operator_id } => {
                return Err(eyre!("unhandled dora reload event: {:?}", operator_id));
            },
            Event::Input { id, metadata: _, data: _ } => {
                match id.as_str() {
                    "tick" => {
                        if has_sent_points {
                            node.send_output_bytes(String::from(output_id).into(), Default::default(), points.len(),&points)?;
                            has_sent_points = false;
                        }
                    },
                    other => {
                        error!("unexpected input: {}", other);
                    }
                }
            },
            Event::InputClosed { id } => {
                info!("dora input {} closed", id);
                return Ok(());
            },
            Event::Error(msg) => {
                return Err(eyre!("received dora error event: {}", msg));
            },
            _ => {
                error!("unkown dora event: {:?}", event);
            }
        }
    }
    Ok(())
}

#[tokio::main]
async fn main() -> eyre::Result<()> {
    re_log::setup_logging();
    let args = Args::try_parse()?;
    let (points, output_id) = match args.format {
        DataFormat::XYZ => {
            let cloud = PointCloudPtr::<PointXYZ>::from_pcd_file(&args.path.to_string_lossy())
                .context(format!("failed to load dora file '{}'", args.path.display()))?;
            (cloud.to_vecs3d_bytes(), DataKind::PointsXYZ.as_output_id())
        }
        DataFormat::XYZI => {
            let cloud = PointCloudPtr::<PointXYZI>::from_pcd_file(&args.path.to_string_lossy())
                .context(format!("failed to load dora file '{}'", args.path.display()))?;
            (cloud.to_vecs3d_bytes_with_intensity(), DataKind::PointsXYZI.as_output_id())
        }
    };
    let (node, events) = DoraNode::init_from_env()?;
    run(node, events, points, output_id)?;
    Ok(())
}